package java113.dataAccess;

/**
 * This class will be used to perform low-level database operations; it will return the necessary objects to 
 * perform all CRUD operations in the database; this serves as an interface of sorts from the applicaiton
 * to the database
 */

//import needed for database calls
import java.sql.*;
import java.util.Properties;

public class DataManager
{
    private Properties properties;

    private Connection m_oConn;
    private boolean m_blCommit;
    private String m_strDatabase;
    private String m_strDriver;
    private String m_strUserName;
    private String m_strPassword;

    /**
     *Empty constructor
     */
    public DataManager(Properties properties) throws Exception
    {

        this.properties = properties;

        setDriver (this.properties.getProperty("db.driver"));
        setDatabase (this.properties.getProperty("db.url"));
        setM_strUserName(this.properties.getProperty("db.username"));
        setM_strPassword(this.properties.getProperty("db.password"));
        init ();

    }

    /**
     * Initializes the database connection for use
     */
    public void init() throws Exception
    {
            Class.forName(this.m_strDriver);
            this.m_oConn = DriverManager.getConnection(m_strDatabase, m_strUserName, m_strPassword);
    }
    
    public void setDriver(String strIn)
    {
        this.m_strDriver = strIn;
    }
    
    public String getDriver()
    {
        return this.m_strDriver;
    }

    /**
     * @return the m_strUserName
     */
    public String getM_strUserName() {
        return m_strUserName;
    }

    /**
     * @param m_strUserName the m_strUserName to set
     */
    public void setM_strUserName(String m_strUserName) {
        this.m_strUserName = m_strUserName;
    }

    /**
     * @return the m_strPassword
     */
    public String getM_strPassword() {
        return m_strPassword;
    }

    /**
     * @param m_strPassword the m_strPassword to set
     */
    public void setM_strPassword(String m_strPassword) {
        this.m_strPassword = m_strPassword;
    }
    
    
    /**
     *Sets the connection object as a property to itself.
     */
    public void setODBCConnection(Connection oConn)
    {
        this.m_oConn = oConn;
    }

    /**
     *Sets the connection object as a property to itself. It also tries to set the
     *current user as the superuser, and if it fails, defaults it to false.
     */
    public void setConnection(Connection oConn)
    {
        this.m_oConn = oConn;
    }

    /**
     *Return the connection object
     */
    public Connection getConnection()
    {
        return this.m_oConn;
    }

    /**
     *Close the connection associated with the object.
     */
    public void closeConnections()
    {
        try
        {
            this.m_oConn.close();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     *Return the database to which the connection is open
     */
    public String getDatabase()
    {
        return this.m_strDatabase;
    }

    /**
     *Set the database to which the connection is open
     */
    public void setDatabase(String aStr)
    {
        this.m_strDatabase = aStr;
    }

    public Statement createStatement() throws Exception
    {
            Statement oStmt;

            //create a new statement object
            oStmt = this.m_oConn.createStatement();
            return oStmt;
    }
    
    /**
     *This is one of the most important methods in this class, and is used throughout
     *the application to execute queries against the database using the current connection.
     *This returns the resultset based on the execution of the query
     */
    public ResultSet executeQuery(Statement oInStmt, String sSQL) throws Exception
    {
        ResultSet oRS;
        
        try
        {
            //execute the query and store the result in a resultset
            try
            {
                oRS = oInStmt.executeQuery(sSQL);
            }
            catch(Exception Ex)
            {
                this.close(oInStmt); // In case we get an error on the query, we want to make sure the statement gets closed
                throw Ex;
            }

            return oRS;

        }
        catch(Exception Ex)
        {
            throw Ex;
        }
    }

    /**
     * This is one of the most important methods which is used to execute ddl-related
     * queries against the database using the current connection.
     * This includes insert, update, create etc.
     */
    public ResultSet executeUpdate(Statement oInStmt, String sSQL) throws Exception
    {
        ResultSet oRS;

        //execute the query and store the result in a resultset
            try
            {
                oInStmt.executeUpdate(sSQL, Statement.RETURN_GENERATED_KEYS);

                oRS = oInStmt.getGeneratedKeys();

                commit();
            }
            catch(Exception Ex)
            {
                this.close(oInStmt); // In case we get an error on the query, we want to make sure the statement gets closed
                throw Ex;
            }

        return oRS;
    }

    /**
     *method to close the statment object and the resultset
     *this frees up the cursor to the database
     */
    public void close(Statement oInStmt) throws Exception
    {
                oInStmt.close();
    }

    /**
     *method that provides transactional support and performs a commit
     */
    public void commit()
    {
        try
        {
            if(this.m_blCommit)
            {
                this.m_oConn.commit();
            }
        }
        catch(SQLException e)
        {
        }
    }

    /**
     *method that provides transactional support and performs a rollback
     */
    public void rollback()
    {
        try
        {
            this.m_oConn.rollback();
        }
        catch(SQLException e)
        {
        }
    }

    /**
     *Returns whether the commit is on/off
     */
    public boolean canCommit()
    {
        return this.m_blCommit;
    }

    /**
     *A boolean flag to turn on/off commit operations. This is used only in certain
     *transmissions and should never be used otherwise
     */
    public void setCommit(boolean aCommit)
    {
        this.m_blCommit = aCommit;
    }

}